/* Copyright (C) 2016-2017 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:lktcipher.cpp
 * Author:guojianchuan
 * Date:2017-05-19

 */

/* 系统头文件 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>

/* 依赖库头文件 */
#include "openssl/evp.h"
#include "openssl/err.h"
#include "openssl/pem.h"

/* 本模块头文件 */
#include "lktcommon.h"
#include "lkterr.h"
#include "lktcipher.h"
#include "zlog.h"

#define MAPPINGID_LEN               5
#define VERSION_LEN                 3
#define KEY_CRC_LEN                 3
#define CIPHER_CRC_LEN              3
#define LKT_ENC_BLOCK_SIZE      (1024*4)
#define CIPHER_TEXT_LEN(cipherText) (strlen(cipherText) - CIPHER_TEXT_IDENTIFY_OFFSET - END_IDENTIFY_LEN)


typedef unsigned char byte;

static unsigned char auchCRCHi[]={
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
 
static unsigned char auchCRCLo[]={
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
    0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
    0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
    0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
    0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
    0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
    0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
    0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
    0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
    0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
    0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
    0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
    0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
    0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
 
static byte _LKT_CRC16_MODBUS(const byte* puchMsg, unsigned short usDataLen)
{
    unsigned char uchCRCHi = 0xFF ;
    unsigned char uchCRCLo = 0xFF ;
    unsigned char uIndex ;
 
    while (usDataLen--)
    {
        uIndex = uchCRCHi ^ *puchMsg++;
        uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
        uchCRCLo = auchCRCLo[uIndex];
    }
    unsigned short result = (uchCRCHi << 8 | uchCRCLo) ;
    byte firstByte = (result >> (8*0)) & 0xff;
    byte secondByte = (result >> (8*1)) & 0xff;
    byte resultByte = firstByte ^ secondByte;
    
    return resultByte;
};


/**
* @Function:LKT_OpenSSLErrString
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 获取openssl错误信息
* @caution: 
*/
char* LKT_OpenSSLErrString (void)
{
    return ERR_error_string(ERR_get_error(),NULL);
}

/**
* @Function:LKT_InitOpenSSL
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 初始化openssl
* @caution: 
*/
int LKT_InitOpenSSL (void)
{
    int ret = ERR_OPENSSL_SUCCESS;
    
    ret = ERR_load_ERR_strings();
    if (ERR_OPENSSL_SUCCESS != ret)
    {
        return ERR_OPENSSL_FAILED;
    }

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LKT_ReleaseOpenSSL
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 释放openssl
* @caution: 
*/
void LKT_ReleaseOpenSSL (void)
{
    return;
}

/**
* @Function:LKT_DecodeBase64
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description:  BASE64解码
* @caution: 
*/
unsigned char* LKT_DecodeBase64 (unsigned char* in, int inlen, unsigned char* out, int *outlen)
{
    if ((NULL == in) || (NULL == out) || (NULL == outlen))
    {
        return NULL;
    }

    /* 计算输入的base64结尾,有多少个'=' */
    int tmpLen     = inlen;
    int equSignNum = 0;
    while ('=' == in[--tmpLen])
    {
        equSignNum++;
    }
    
    /* Decode base64(输入不接受换行符)*/
    int len = 0;
    len = EVP_DecodeBlock(out, in, inlen);
    if (-1 == len)
    {
        return NULL;
    }

    *outlen = len - equSignNum;
    
    return out;
}

/**
* @Function:LKT_ConvertToBase64
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 转换成BASE64
* @caution: 
*/
unsigned char* LKT_ConvertToBase64(unsigned char *in, int inlen, unsigned char *out,  int *outlen, int newLine)
{
    if ((NULL == in) || (NULL == out) || (NULL == outlen))
    {
        return NULL;
    }

    int baseLen = 0;
    baseLen = EVP_EncodeBlock(out, in, inlen);

    if (1 == newLine)
    {
        out[baseLen] = '\n';
        out[baseLen + 1] = 0;
        baseLen++;
    }
    
    *outlen = baseLen;

    return out;
}

/**
* @Function:LKT_ConvertToHex
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 转换成HEX
* @caution: 
*/
unsigned char* LKT_ConvertToHex(unsigned char *in, int inlen, unsigned char *out,  int *outlen)
{
    if ((NULL == in) || (NULL == out) || (NULL == outlen))
    {
        return NULL;
    }

    int i = 0;

    for (i = 0; i < inlen; i++)
    {
        snprintf ((char*) (out + 2 * i), 2 + 1, "%.02x", in[i]);
    }

    *outlen = 2 * i;

    return out;
}

/**
* @Function:LKT_Digest
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 摘要算法接口(algoritm可以是MD5,SHA1,SHA256等)
* @caution: 
*/
int LKT_Digest(const char* algorithm, const char *in, unsigned char *out)
{
    if ((NULL == algorithm) || (NULL == in) || (NULL == out))
    {
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_OPENSSL_SUCCESS;
    
    const EVP_MD *md = NULL;
    md = EVP_get_digestbyname(algorithm);
    if (NULL == md)
    {
        return ERR_OPENSSL_INVALID_DIGEST;
    }

    int outlen = 0;
    
    EVP_MD_CTX *digestctx = NULL;
    do 
    {
        digestctx = EVP_MD_CTX_new ();
        if (NULL == digestctx)
        {
            ret = ERR_COMMON_NO_MEM;
            break;
        }
        
        ret = EVP_DigestInit_ex(digestctx, md, NULL);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_DIGEST_INIT_FAILED;
            break;
        }
        
        /* Update digest */
        ret = EVP_DigestUpdate(digestctx, in, strlen(in));
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_DIGEST_UPDATE_FAILED;
            break;
        }
        
        /* Output digest */
        ret = EVP_DigestFinal_ex(digestctx, out, (unsigned int *) &outlen);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_DIGEST_FINAL_FAILED;
            break;
        }
    } while (0);

    EVP_MD_CTX_free(digestctx);

    if (ERR_OPENSSL_SUCCESS == ret)
    {
        ret = ERR_COMMON_SUCCESS;
    }

    return ret;
}

/**
* @Function:LKT_Hmac
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: HMAC接口(algorithm可以MD5,SHA1,SHA256)
* @caution: 
*/
int LKT_Hmac(const char* algorithm, unsigned char *in, unsigned char* key, unsigned char *out, int *outlen)
{
    if ((NULL == algorithm) || (NULL == in) || (NULL == in) || (NULL == out) || (NULL == outlen))
    {
        return ERR_COMMON_INVALID_PARAM;
    }
    
    int ret = ERR_OPENSSL_SUCCESS;
    
    const EVP_MD *md = NULL;
    md = EVP_get_digestbyname(algorithm);
    if (NULL == md)
    {
        return ERR_OPENSSL_INVALID_DIGEST;
    }


    EVP_PKEY *macKey      = NULL;
    EVP_MD_CTX *digestctx = NULL;
    do
    {
        /* Init digest */
        digestctx = EVP_MD_CTX_new ();
        if (NULL == digestctx)
        {
            ret = ERR_COMMON_NO_MEM;
            break;
        }

        ret = EVP_DigestInit_ex(digestctx, md, NULL);
        if (ERR_OPENSSL_SUCCESS  != ret)
        {
            ret = ERR_OPENSSL_DIGEST_INIT_FAILED;
            break;
        }

        macKey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, strlen((char*) key));
        if (NULL == macKey)
        {
            ret = ERR_OPENSSL_DIGEST_NEW_MAC_KEY_FAILED;
            break;
        }

        ret = EVP_DigestSignInit(digestctx, NULL, md, NULL, macKey);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_DIGEST_SIGN_INIT_FAILED;
            break;
        }

        ret = EVP_DigestSignUpdate(digestctx, in, strlen((char*) in));
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_DIGEST_SIGN_UPDATE_FAILED;
            break;
        }

        ret = EVP_DigestSignFinal(digestctx, out, (size_t*) outlen);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_DIGEST_SIGN_FINAL_FAILED;
            break;
        }
    } while (0);

    EVP_MD_CTX_free (digestctx);
    EVP_PKEY_free (macKey);
    
    if (ERR_OPENSSL_SUCCESS == ret)
    {
        ret = ERR_COMMON_SUCCESS;
    }

    return ret;
}

/**
* @Function:LKT_AESCrypt
* @Author: guojianchuan/max
* @Date: 2017-06-26
* @Description: AES encrypt/decrypt
* @caution: Algo format: Algo-bitnum-mode eg:"AES-128-CFB",
*/
int LKT_AESCrypt (CIPHER_AES_S *cipher)
{
    unsigned char *in  = NULL;
    unsigned char *out = NULL;
    int tmplen = 0;
    int inlen  = 0;
    int outlen = 0;

    if (NULL == cipher)
    {
        return ERR_COMMON_INVALID_PARAM;
    }
#if 0
    if (cipher->mode == 1)
    {
        printf ("encrypt----------------------------------\n");
        printf ("inlen:%d\n", cipher->inlen);
        printf ("input:%s\n", cipher->input);
        int i = 0;
        printf ("key:");
        for (i = 0; i < 32; i ++ )
        {
            printf ("%.2x", cipher->key[i]);
        }
        printf (
"\n");
        printf ("iv:");
        for (i = 0; i < 16; i ++ )
        {
            printf ("%.2x", cipher->iv[i]);
        }
        printf (
"\n");
    }
    else
    {
        printf ("decrypt----------------------------------\n");
        printf ("inlen:%d\n", cipher->inlen);
        printf ("input:");
        int i = 0;
        for (i = 0; i < cipher->inlen; i ++ )
        {
            printf ("%.2x", cipher->input[i]);
        }
        printf ("\n");
        printf ("key:");
        for (i = 0; i < 32; i ++ )
        {
            printf ("%.2x", cipher->key[i]);
        }
        printf (
"\n");
        printf ("iv:");
        for (i = 0; i < 16; i ++ )
        {
            printf ("%.2x", cipher->iv[i]);
        }
        printf (
"\n");
    }
    #endif
    int ret = ERR_OPENSSL_SUCCESS;
    
    /* Check algo */
    const EVP_CIPHER *evpCipher = NULL;
    evpCipher = EVP_get_cipherbyname(cipher->algo);
    if (NULL == evpCipher)
    {
        ret = ERR_OPENSSL_INVALID_CIPHER;
        return ret;
    }
    
    /* encrypt/decryt */
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();

    /* init algo */
    ret = EVP_CipherInit_ex(ctx, evpCipher, NULL, cipher->key, cipher->iv, cipher->mode);
    if (ERR_OPENSSL_SUCCESS != ret)
    {
        return ERR_OPENSSL_CIPHER_INIT_FAILED;
    }
    
    in    = cipher->input;
    inlen = cipher->inlen;
    out   = cipher->output;
    cipher->outlen = 0;

    while(inlen > 0)
    {
        outlen = 0;
        tmplen = (inlen > LKT_ENC_BLOCK_SIZE) ? LKT_ENC_BLOCK_SIZE : inlen;
        /* encrypt/decrypt */
        ret = EVP_CipherUpdate(ctx, out, &outlen, in, tmplen);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            EVP_CIPHER_CTX_free (ctx);
            return  ERR_OPENSSL_CIPHER_UPDATE_FAILED;
        }

        in += tmplen;
        inlen -= tmplen;
        out += outlen;
        cipher->outlen += outlen;
    }

    ret = EVP_CipherFinal_ex(ctx, out, &outlen);
    if (ERR_OPENSSL_SUCCESS != ret)
    {
        EVP_CIPHER_CTX_free (ctx);
        return ERR_OPENSSL_CIPHER_FINAL_FAILED;
    }

    cipher->outlen += outlen;

    /* don't forget free */
    EVP_CIPHER_CTX_free (ctx);

    if (ERR_OPENSSL_SUCCESS == ret)
    {
        ret = ERR_COMMON_SUCCESS;
    }
    #if 0
    if (cipher->mode == 1)
    {
        int i = 0;
        printf("outputlen:%d\n", cipher->outlen);
        printf ("output:");
        for (i = 0; i < cipher->outlen; i ++ )
        {
            printf ("%.2x", cipher->output[i]);
        }
        printf ("\n");
        printf ("+++++++++++++++++++++++++++++++++++\n");
    }
    else
    {
        printf ("output:%s\n", cipher->output);
        printf ("+++++++++++++++++++++++++++++++++++\n");
    }
    #endif

    return ret;
}

/**
* @Function:LOCKET_CIPHER_RSADecrypt
* @Author: guojianchuan/max
* @Date: 2017-06-26
* @Description: RSA decrypt with private key (PADDING: PKCS-OAEP)
* @caution:
*/
int LKT_RSADecrypt (CIPHER_RSA_S *cipher)
{
    int ret = ERR_COMMON_SUCCESS;
    ERR_load_ERR_strings();

    EVP_PKEY *pkey = NULL;
    EVP_PKEY_CTX *pkctx = NULL;
    unsigned char* outbuf = NULL;
    const unsigned char *p = NULL;
    do 
    {
        p = cipher->pucKey;
        pkey = d2i_AutoPrivateKey(NULL, &p, cipher->keyLen);
        if (NULL == pkey)
        {
            ret = ERR_OPENSSL_CIPHER_D2I_PVKEY_FAILED;
            break;
        }
        
        pkctx = EVP_PKEY_CTX_new(pkey, NULL);
        if (NULL == pkctx)
        {
            ret = ERR_OPENSSL_CIPHER_RSA_NEWKEY_FAILED;
            break;
        }

        ret = EVP_PKEY_decrypt_init(pkctx);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_CIPHER_RSA_INIT_FAILED;
            break;
        }
        
        ret = EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_OAEP_PADDING);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_CIPHER_RSA_SET_PAD_FAILED;
            break;
        } 

        EVP_PKEY *pkey1 = EVP_PKEY_CTX_get0_pkey (pkctx);
        RSA *rsa = EVP_PKEY_get1_RSA(pkey1);

        int outlen = RSA_size (rsa);
        outbuf = (unsigned char*) malloc(outlen);
        if (NULL == outbuf)
        {
            ret = ERR_COMMON_NO_MEM;
            break;
        }
        memset (outbuf, 0, outlen);

        ret = EVP_PKEY_decrypt(pkctx, outbuf, (size_t*) &outlen, cipher->input, cipher->inlen);
        if (ERR_OPENSSL_SUCCESS != ret)
        {
            ret = ERR_OPENSSL_CIPHER_RSA_DECRYPT_FAILED;
            break;
        }

        memcpy (cipher->output, outbuf, outlen);
        cipher->outlen = outlen;
    } while (0);
        
    free (outbuf);
    EVP_PKEY_CTX_free(pkctx);

    if (ERR_OPENSSL_SUCCESS != ret)
    {
        return ret;
    }
    
    return ERR_COMMON_SUCCESS;
}

int LKT_PBKDF2 
(
    const char *pass, 
    int passlen,
    const unsigned char *salt, 
    int saltlen, 
    int iter,int keylen, unsigned char *out
)
{

    int ret = PKCS5_PBKDF2_HMAC_SHA1(pass, passlen, salt, saltlen, iter, keylen, out); 
    if (ERR_OPENSSL_SUCCESS != ret)
    {
        return ERR_OPENSSL_PBKDF2_FAILED;
    }

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LKT_BuildCipherFormat
* @Author: guojianchuan/max
* @Date: 2016-06-09
* @Description: 构造cipher format
* @caution: 
*/
void LKT_BuildCipherFormat (CIPHER_FORMAT_S *cipherFormat)
{        
    byte keyCRC        = _LKT_CRC16_MODBUS ((byte*) cipherFormat->key, strlen(cipherFormat->key));
    byte cipherTextCRC = _LKT_CRC16_MODBUS (cipherFormat->cipherText, cipherFormat->cipherLen);
    
    snprintf ((char*) cipherFormat->formated, cipherFormat->formatBufLen, "%s%02d%02x%04d%02x%s%c",
              START_IDENTIFY, cipherFormat->version, keyCRC,cipherFormat->mappingid, 
              cipherTextCRC, (char*) cipherFormat->cipherText, END_IDENTIFY);
    
    return;
}


/**
* @Function:_LKT_CheckVersion
* @Author: guojianchuan/max
* @Date: 2016-06-09
* @Description: 检查版本号是否正确
* @caution: 
*/
static int _LKT_CheckVersion (int version)
{
    int correct = 1;
    
    switch (version)
    {
        case VERSION_0:
        {
            break;
        }
        case VERSION_1:
        {
            break;
        }
        default:
        {
            correct = 0;
            break;
        }
    }

    return  correct;
}

/**
* @Function:LKT_GetCipherText
* @Author: guojianchuan/max
* @Date: 2016-06-09
* @Description: 在format text中找到密文
* @caution: 
*/
void LKT_GetCipherText (const char* formatText, char* cipherText)
{
    strncpy (cipherText, formatText + CIPHER_TEXT_IDENTIFY_OFFSET, CIPHER_TEXT_LEN(formatText));
}

/**
* @Function:LKT_GetMappingID
* @Author: guojianchuan/max
* @Date: 2016-06-09
* @Description: 在format text中找到mappingid
* @caution: 
*/
int LKT_GetMappingID (const char* formatText)
{
    char szMappingID[MAPPINGID_LEN] = {0};
    snprintf (szMappingID, sizeof (szMappingID), "%s", formatText + MAPPINGID_IDENTIFY_OFFSET);
    unsigned int mappingid = (int) strtoull (szMappingID, NULL, 10);

    return mappingid;
}

/**
* @Function:LKT_CheckCipherFormat
* @Author: guojianchuan/max
* @Date: 2017-06-06
* @Description: 检查密文格式是否正确
* @caution:  cipherText, key都是base64格式
*/
int LKT_CheckCipherFormat (const char* formatText, const char *key)
{
    int ret = ERR_COMMON_SUCCESS;

    do 
    {
        /* 检查头 */
        if (0 != strncmp (formatText + START_IDENTIFY_OFFSET, START_IDENTIFY, strlen(START_IDENTIFY)))
        {   
            dzlog_error("head at cipher index[%d] length[%d] doesn't match %s", START_IDENTIFY_OFFSET, (int)strlen(START_IDENTIFY), START_IDENTIFY);
            ret = ERR_INVALID_START_IDENTIFY;
            break;
        }

        /* 检查版本号 */
        char szVersion[VERSION_LEN] = {0};
        snprintf (szVersion, sizeof (szVersion), "%s", formatText + VERSION_IDENTIFY_OFFSET);
        int version = atoi (szVersion);
        if (0 == _LKT_CheckVersion (version))
        {
            dzlog_error("version [%s] does match", szVersion );
            ret = ERR_INVALID_VERSION_IDENTIFY;
            break;
        }

        /* 检查key crc */
        char szKeyCRC[KEY_CRC_LEN] = {0};
        snprintf (szKeyCRC, sizeof (szKeyCRC), "%s", formatText + KEYCRC_IDENTIFY_OFFSET);
        unsigned int keyCRC1 = (unsigned int) strtoull (szKeyCRC, NULL, 16);
        byte keyCRC2 = _LKT_CRC16_MODBUS ((unsigned char*) key, strlen(key));
        if (keyCRC1 != (unsigned int) keyCRC2)
        {
            dzlog_error("key CRC [%s] check error, keyCRC1[%.2x],keyCRC2[%.2x]", szKeyCRC, keyCRC1, keyCRC2);
            ret = ERR_INVALID_KEY_CRC;
            break;
        }
        
        /* 不检查mappingid */

        /* 检查密文CRC */
        char szCipherCRC[CIPHER_CRC_LEN] = {0};
        snprintf (szCipherCRC, sizeof (szCipherCRC), "%s", formatText + CIPHERCRC_IDENTIFY_OFFSET);
        unsigned int cipherCRC1 = (unsigned int) strtoull (szCipherCRC, NULL, 16);
        byte cipherCRC2 = _LKT_CRC16_MODBUS ((byte*) (formatText + CIPHER_TEXT_IDENTIFY_OFFSET), 
                                             CIPHER_TEXT_LEN(formatText));
        if (cipherCRC1 != (unsigned int) cipherCRC2)
        {
            dzlog_error("cipher CRC [%s] check error, cipherCRC1[%.2x], cipherCRC2[%.2x]", szCipherCRC, cipherCRC1, cipherCRC2);
            ret = ERR_INVALID_CIPHER_CRC;
            break;
        }
        
        /* 检查尾 */
        int formatLen = strlen(formatText);
        if (formatText[formatLen] == END_IDENTIFY)
        {
            dzlog_error("cipher tail check error");
            ret = ERR_INVALID_END_IDENTIFY;
            break;
        }
    } while (0);

    if (ERR_COMMON_SUCCESS != ret)
    {
        dzlog_error("formatedText:%s, key:%s", formatText, key);
        return ret;
    }


    return ret;
}

/**
* @Function:LKT_CheckStartIdentify
* @Author: guojianchuan/max
* @Date: 2017-06-29
* @Description: 检查前置Identify是否正确
* @caution: 
*/
int LKT_CheckStartIdentify (const char* field)
{
    int valid = 0;
    if (0 == strncmp (field, START_IDENTIFY, strlen(START_IDENTIFY)))
    {
        valid = 1;
    }

    return valid;
}

